package dawn.service.async;

import java.lang.reflect.Method;

import dawn.server.GameServer;


/**
 * 异步的回调抽象类,你需要完成的是： 
 * 1、异步执行的操作，在Runnable接口的run方法中实现，并且在方法的最后，将此对象放入到回调队列
 * 2、回调操作，在ICallback接口的callback实现，调用callbackObject的方法来完成回调操作
 * 你可以通过setCallbackObject来设置回调的对象，在自己的实现中，可以调用callbackObject的任何方法
 * 
 * @author huangxiao
 * 
 * @param <V>
 */
public class AsyncCall implements Runnable, ICallback {

    private Object callObject;
    private Object callbackObject;

    private Object[] callParameters; // 调用API传递的参数
    private Object[] callbackParameters; // 回调时传递的参数，第一个参数固定为API调用的返回结果
    private String callMethodName; // 调用的方法名
    private String callbackMethodName; // 回调的方法名
    private Method callMethod; // 需要调用的方法
    private Method callbackMethod; // 需要回调的方法

    private Object result; // 调用的返回结果

    public AsyncCall(Object callObject, String callMethodName) {
        setCallObject(callObject);
        setCallMethodName(callMethodName);
    }

    public AsyncCall(Object callObject, String callMethodName,
            Object callbackObject, String callbackMethodName) {
        setCallObject(callObject);
        setCallMethodName(callMethodName);
        setCallbackObject(callbackObject);
        setCallbackMethodName(callbackMethodName);
    }

    /**
     * 对这次异步调用进行回调
     */
    @Override
    public void callback() {
        try {

            callbackMethod.invoke(this.getCallbackObject(), callbackParameters);

        } catch (Exception e) {
            System.out.println("Error:" + callObject.getClass().getName() + "."
                    + callMethodName);

            if (callbackObject != null)
                System.out.println("Error:"
                        + callbackObject.getClass().getName() + "."
                        + callbackMethodName);
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        callMethod = getMethod(callMethodName, callObject);

        // null check
        if (callMethod == null) {
            System.out.println(callMethodName.toString() + ": dao method or object is null!");
            return;
        }

        try {
            callMethod.setAccessible(true);
            result = callMethod.invoke(callObject, callParameters);
            prepareCallback(); // 准备回调，预处理

        } catch (Exception e) {
            System.out.println("Error:" + callObject.getClass().getName() + "."
                    + callMethodName);

            if (callbackObject != null)
                System.out.println("Error:"
                        + callbackObject.getClass().getName() + "."
                        + callbackMethodName);
            e.printStackTrace();
        }
    }

    /**
     * 回调前的预处理
     */
    private void prepareCallback() {

        // 在不为空的情况下才进行回调
        callbackMethod = getMethod(callbackMethodName, callbackObject);
        if (callbackMethod == null)
            return;

        // 回调，默认第一个参数为dao返回的结果，后面才是设置的参数
        callbackMethod.setAccessible(true);

        Object[] args = null;

        // 确定参数的长度
        int argsLen = 0;
        if (callbackParameters != null)
            argsLen = callbackParameters.length;

        if (callMethod.getReturnType() != void.class) {
            argsLen++;
        }

        // 组织参数
        if (argsLen != 0) {
            args = new Object[argsLen];
            args[0] = result;
            if (callbackParameters != null)
                System.arraycopy(callbackParameters, 0, args, 1,
                        callbackParameters.length);
        }

        callbackParameters = args;

//        GameServer.getIns().get(AsyncService.class).;  TODO
    }

    private Method getMethod(String name, Object obj) {
        if (name == null || obj == null)
            return null;
        Method[] methods = obj.getClass().getMethods();
        for (Method m : methods) {
            if (m.getName().equals(name))
                return m;
        }

        return null;
    }

    /* -----------------getters and setters----------------- */
    public Object getCallbackObject() {
        return callbackObject;
    }

    public void setCallbackObject(Object callbackObject) {
        this.callbackObject = callbackObject;
    }

    public Object getCallObject() {
        return callObject;
    }

    public void setCallObject(Object callObject) {
        this.callObject = callObject;
    }

    public Object[] getCallParameters() {
        return callParameters;
    }

    public void setCallParameters(Object... callParameters) {
        this.callParameters = callParameters;
    }

    public Object[] getCallbackParameters() {
        return callbackParameters;
    }

    public void setCallbackParameters(Object... callbackParameters) {
        this.callbackParameters = callbackParameters;
    }

    public Method getCallMethod() {
        return callMethod;
    }

    public void setCallMethod(Method callMethod) {
        this.callMethod = callMethod;
    }

    public Method getCallbackMethod() {
        return callbackMethod;
    }

    public void setCallbackMethod(Method callbackMethod) {
        this.callbackMethod = callbackMethod;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    public String getCallMethodName() {
        return callMethodName;
    }

    public void setCallMethodName(String callMethodName) {
        this.callMethodName = callMethodName;
    }

    public String getCallbackMethodName() {
        return callbackMethodName;
    }

    public void setCallbackMethodName(String callbackMethodName) {
        this.callbackMethodName = callbackMethodName;
    }
}
